<template>
  <div class="_container" ref="conteainerRef">
    <canvas ref="pdfCanvas"></canvas>
    <div class="filed_container" v-for="field in fields" :id="`point_${field.name}`" :key="field.name"
      :style="fieldStyle(field)" @click="focusField(field)">
      <span>{{ field.name }}</span>
    </div>
    <!-- 右侧悬浮表单 -->
    <div class="form_container" ref="formContainerRef"> </div>
  </div>
</template>

<script setup lang="ts">
import { onMounted, ref, reactive } from 'vue'
import { getDocument, GlobalWorkerOptions } from 'pdfjs-dist'

GlobalWorkerOptions.workerSrc = 'https://cdn.jsdelivr.net/npm/pdfjs-dist@4.3.136/build/pdf.worker.min.mjs'

type FieldType = {
  name: string
  type: string
  x: number
  y: number
  width: number
  height: number
}

const props = defineProps({
  currentPage: {
    type: Number as PropType<number>,
    default: 1
  }
})

/**
 * 向外暴露数据
 */
const emits = defineEmits<{
  (e: 'loadPdf', value: number): void // 加载PDF事件，可以传递总页码
  (e: 'renderForm', value: FieldType[]): void // 触发渲染表单事件
  (e: 'focusField', value: FieldType): void // 触发字段聚焦事件
}>()


const pdfURL = ref<string>('/pdfs/example.pdf') // 自持base64 + url

const pdfCanvas = ref<any>()
const conteainerRef = ref<HTMLElement>()
const pdfDoc = ref<any>(null)
const fields = reactive<FieldType[]>([])
const pageNumbs = ref(1)
const currentScale = ref(1)
const formContainerRef = ref<HTMLElement>()
const currentPage = ref(props.currentPage)

const activeFiled = ref<string>('')

let pagePDfDoc: any

const loadPdf = async () => {
  try {
    const loadingTask = getDocument(pdfURL.value)
    const pageObject = await loadingTask.promise
    pdfDoc.value = pageObject

    if (!pdfDoc.value) {
      throw new Error('PDF文档加载错误')
    }
    pagePDfDoc = pageObject
    pageNumbs.value = await pdfDoc.value.numPages
    emits('loadPdf', pageNumbs.value)
    console.log(`PDF 加载: ${pageNumbs.value} 页`)
  } catch (error) {
    console.error('加载PDF失败:', error)
  }
}

const pdf_div_width = ref('')
const ratioGlobal = ref(0)

const renderPdf = async (pageNum = 1) => {
  try {
    const page = await pagePDfDoc.getPage(pageNum)
    const canvas = pdfCanvas.value!
    const ctx = canvas.getContext('2d')
    const dpr = window.devicePixelRatio || 1
    const bsr =
      ctx.webkitBackingStorePixelRatio ||
      ctx.mozBackingStorePixelRatio ||
      ctx.msBackingStorePixelRatio ||
      ctx.oBackingStorePixelRatio ||
      ctx.backingStorePixelRatio ||
      1
    const ratio = dpr / bsr
    ratioGlobal.value = ratio
    const viewport = page.getViewport({ scale: currentScale.value * 2 })
    canvas.width = viewport.width * ratio
    canvas.height = viewport.height * ratio
    canvas.style.width = viewport.width + 'px'
    pdf_div_width.value = viewport.width + 'px'
    conteainerRef.value!.style.width = viewport.width + 'px'
    conteainerRef.value!.style.height = viewport.height + 'px'

    canvas.style.height = viewport.height + 'px'
    ctx.setTransform(ratio, 0, 0, ratio, 0, 0)
    const renderContext = {
      canvasContext: ctx,
      viewport: viewport
    }

    await extractFields(pageNum) // 提取当前页面的字段
    emits('renderForm', fields) // 派发事件出去,渲染表单自定义逻辑
    await page.render(renderContext).promise
  } catch (error) {
    console.error(`渲染报错 ${pageNum}: ${error}`)
  }
}

const extractFields = async (pageNum: any) => {
  if (!pagePDfDoc) {
    console.error('PDF对象不存在.')
    return
  }

  fields.splice(0, fields.length) // 清空现有字段数据

  try {
    const page = await pagePDfDoc.getPage(pageNum)
    const viewport = page.getViewport({ scale: currentScale.value })
    const annotations = await page.getAnnotations()
    const _scale = currentScale.value * 2

    function convertToCanvasCoordinates(
      pdfX: number,
      pdfY: number,
      pageHeight: number,
      scale: number
    ) {
      const adjustedPdfY = pageHeight - pdfY // 将 PDF 坐标系转换为 canvas 坐标系
      const canvasX = pdfX * scale
      const canvasY = adjustedPdfY * scale
      return { x: canvasX, y: canvasY }
    }

    annotations.forEach((annot: any) => {
      if (annot.subtype === 'Widget' && annot.fieldName && annot.fieldType === 'Tx') {
        const { x, y } = convertToCanvasCoordinates(
          annot.rect[0],
          annot.rect[1],
          viewport.height,
          _scale
        )
        fields.push({
          name: annot.fieldName,
          type: annot.fieldType,
          x,
          y,
          width: (annot.rect[2] - annot.rect[0]) * _scale,
          height: (annot.rect[3] - annot.rect[1]) * _scale
        })

        // 在 form_container 中追加表单
        generateForm(annot)
      }
    })
  } catch (error) {
    console.error(`字段提取失败 ${pageNum}: ${error}`)
  }
}

// @ts-ignore
const generateForm = (annot: { fieldName: string;[key: string]: any }) => {
  const form = document.createElement('div')
  form.id = `field_${annot.fieldName}`
  const label = document.createElement('label')
  const input = document.createElement('input')


  /**  Input反向联动表单滚动  */
  input.addEventListener('click', (e) => {
    e.preventDefault()
    e.stopPropagation()

    const pointItem = document.getElementById(`point_${annot.fieldName}`)
    if (pointItem) {
      activeFiled.value = annot.fieldName
      pointItem?.focus()
      pointItem.scrollIntoView({ behavior: 'auto', block: 'center', inline: 'center' })
    }
  })


  input.id = `input_${annot.fieldName}`
  input.type = 'text'
  input.style.backgroundColor = 'transparent'
  input.style.border = '1px solid #e2e2e2'
  label.innerText = annot.fieldName
  label.style.color = 'red'
  form.appendChild(label)
  form.appendChild(input)
  formContainerRef.value!.appendChild(form)
}

const fieldStyle = (field: FieldType) => {
  return {
    position: 'absolute',
    left: `${field.x}px`,
    top: `${field.y - field.height}px`, // 偏移高度
    width: `${field.width}px`,
    height: `${field.height}px`,
    border: `1px dashed ${activeFiled.value === field.name ? '#00acff' : '#a6a6a6'}`,
    boxSizing: 'border-box', // 确保边框内计算尺寸
    fontSize: '14px',
    background: activeFiled.value === field.name ? 'rgba(98,206,255,.3)' : 'hsla(0,0%,65.1%,.3)',
    cursor: 'pointer'
  } as any
}

const focusField = (field: FieldType) => {
  // console.log(`Field focused: ${field.name}`)
  activeFiled.value = field.name
  const dom = document.getElementById(`input_${field.name}`)
  dom?.focus()
  emits('focusField', field) // 暴露聚焦事件
}

/**
 * 向外提供翻页功能
 */
const prevPage = () => {
  if (currentPage.value > 1) {
    currentPage.value--
    renderPdf(currentPage.value)
  }
}
const nextPage = () => {
  if (currentPage.value < pageNumbs.value) {
    currentPage.value++
    renderPdf(currentPage.value)
  }
}
onMounted(() => {
  loadPdf().then(() => {
    renderPdf(currentPage.value) // 默认渲染第一页
  })
})

defineExpose({
  prevPage,
  nextPage
})
</script>

<style scoped>
* {
  padding: 0;
  margin: 0;
}

body {
  display: flex;
  justify-content: center;
}

._container {
  position: relative;
  width: 100%;
  height: 100%;
}

.form_container {
  position: fixed;
  top: 50px;
  right: 0;
  z-index: 999;
  width: 200px;
  height: 500px;
  overflow-y: scroll;
  background: #fff;
  border: 1px solid blue;
}

.filed_container {
  display: flex;
  align-items: center;
}

.form_input {
  width: 100;
  height: 50px;
  border: 1px solid #e2e2e2;
}

canvas {
  position: absolute;
  top: 0;
  left: 0;
}

input {
  position: absolute;
}
</style>
